/*
 * ============================================================================
 * ============================================================================
 */

#include "SMSEngine.h"
#include <msvids.h> // Folder Ids
#include <txtrich.h> // CRichText
#include <smut.h>
#include <eikdef.h>
#include <eikenv.h>

#ifdef EKA2
#include <rsendas.h>
#include <rsendasmessage.h>
#endif

#include "SmsEnginePanics.pan"

#ifdef __WINS__
const TMsvId KObservedFolderId = KMsvDraftEntryId;
#else
const TMsvId KObservedFolderId = KMsvGlobalInBoxIndexEntryId;
#endif

const TMsvId KInbox = KMsvGlobalInBoxIndexEntryId;
const TMsvId KOutbox = KMsvGlobalOutBoxIndexEntryId;
const TMsvId KDrafts = KMsvDraftEntryId;

const TInt KDelayTime = 1000 * 3000;
const TInt KErrMsgLength = 20;
_LIT(KEmptyMsg,"");

//  CONSTRUCTION AND DESTRUCTION
CSmsEngine* CSmsEngine::NewL( MSmsEngineObserver& aObserver )
{
    CSmsEngine* self = CSmsEngine::NewLC( aObserver );
    CleanupStack::Pop( self );
    return self;
}

CSmsEngine* CSmsEngine::NewLC( MSmsEngineObserver& aObserver )
{
    CSmsEngine* self = new ( ELeave ) CSmsEngine( aObserver );
    CleanupStack::PushL( self );
    self->ConstructL( );
    return self;
}

CSmsEngine::~CSmsEngine( )
{
    delete iMsvEntry;
    iMsvEntry = NULL;

    delete iMsvSession;
    iMsvSession = NULL;

    delete iEngine;
    iEngine = NULL;
}

CSmsEngine::CSmsEngine( MSmsEngineObserver& aObserver ) :
    iObserver( aObserver )
{
}

void CSmsEngine::ConstructL( )
{
    iAutomaticDelete = EFalse;

    iListeningForIncoming = ETrue;

    iEngine = CSMSMtmsEngine::NewL( *this );

    // SMS automatic receiving needs a session to the mfging server
    iMsvSession = CMsvSession::OpenAsyncL( *this );

    // _eikon_env = CEikonEnv::Static( );
}

//listening for incoming Message
void CSmsEngine::HandleSessionEventL( TMsvSessionEvent aEvent, TAny* aArg1, TAny* aArg2, TAny* /*aArg3*/)
{
    switch( aEvent )
    {
        //One way to make sure that the Message has been sent
        //is to watch the sent items folder.
        //The sent SMS could also be deleted here

        //case EMsvEntriesMoved:
        //{
        //Check if our Message has gone to the sent items
        //if (aArg2 && (*(static_cast<TMsvId*>(aArg2)) == KMsvSentEntryId) )
        //	  {
        //    CMsvEntrySelection* entries =
        //    	static_cast<CMsvEntrySelection*>(aArg1);

        //Note: this doesn't work if the Message has been waiting
        //to be sent and has been sent after a boot.
        //In that case the deletion should be done when the server is
        //ready again and the sent items (and also drafts) is searched.
        //    if (entries && iSentMessageId == entries->At(0))
        //        {
        //        Model().State(ESmsEngineSent);
        //if it's our Message and we're supposed to delete it
        //		  if( iAutomaticDeleteFromSentItems ) //note a new flag
        //    		  {
        //			  iMsvSession->RemoveEntry(iSentMessageId);
        //    		  }
        //        }
        //    iObserver.MessageSent();
        //	  }
        //break;
        //}
        case EMsvServerReady:
            // Initialise iMsvEntry
            if( !iMsvEntry )
            {
                iMsvEntry = CMsvEntry::NewL( *iMsvSession, KInbox, TMsvSelectionOrdering( ) );

                //Sent SMS might be left to drafts if the phone was booted during
                //SMS sending.

                //Engine could also be used for retrieving a response for
                //a Message. Response could arrive after phone has been booted
                //so it should be checked here.
                //Now that we're ready to handle Messages we could check
                //if there's a response in the inbox.
                //This could be the case only if the Message has been
                //sent and the phone has been booted while waiting.

                //The same applies to sending and deleting the sent Message
                //Sent Message could be left in the sent items if the phone
                //was booted before the sent Message had been removed.

                //for these situations the engine should be implemented as a
                //state machine and engines state should be checked on initialization.
                //Of course there might be multiple clients (for example if engine
                //implementation would be a server) so there's a need to
                //have own state of each client (as in server session).

                //implementation could be something like
                //TSmsEngineState state = Model().State();

                //if( state == ESmsEngineSending )
                //	{

                //Make sure that the sent Message will be deleted.
                //TBool found(EFalse);

                //TRAPD(error, SearchForSentMessageFromSentMessagesL(found) );

                //if ( !found )
                //	{
                //If the sent Message is stuck in drafts
                //	TRAP(error, SearchForSentMessageFromDraftsL(found) );
                //If this is the case then resend!
                //	}

                //if the Message is somehow mystically lost,
                //then it's handled as the Message has been sent.
                //if( !found )
                //	{
                //	Model().State(ESmsEngineSMSSent);
                //	}
                //  }

            }
            break;

        case EMsvEntriesCreated:
            // Only look for changes in the Inbox
            if( aArg2 && *( static_cast< TMsvId* > ( aArg2 ) ) == KObservedFolderId )
            {
                CMsvEntrySelection* entries = static_cast< CMsvEntrySelection* > ( aArg1 );
                if( entries->Count( ) >= 1 )
                {
                    iNewMessageId = entries->At( 0 );
                }
                else
                {
                    Panic( ESmsEngineInternal );
                }
            }
            break;

        case EMsvEntriesChanged:
            //Look for changes. When using the emulator observed folder is
            //drafts, otherwise inbox.
            //Also a check for the iListeningForIncoming is made
            if( aArg2 && *( static_cast< TMsvId* > ( aArg2 ) ) == KObservedFolderId && iListeningForIncoming )
            {
                CMsvEntrySelection* entries = static_cast< CMsvEntrySelection* > ( aArg1 );

                //improvement for the case of receiving a response

                //When the phone is booted and the Message has arrived before
                //application has started it only receives notification of
                //changes
                //in the inbox (when the user reads the Message!), not the
                //creation where these id is initialized.
                //therefore we check when changes occur that if the iNewMessageID
                //is -1 we make client handle the response.
                //Check the id of the Message (iNewMessageId is set
                //in case of EMsvEntriesCreated)

                //code could be something like:
                //if ( entries && (iNewMessageId == entries->At(0) || iNewMessageId == -1) )

                if( entries->Count( ) < 1 )
                {
                    Panic( ESmsEngineInternal );
                }
                else if( iNewMessageId == entries->At( 0 ) )
                {

                    if( !iMsvEntry )
                    {
                        Panic( ESmsEngineNotInitialized );
                        return;
                    }

                    // Set entry context to the new Message
                    iMsvEntry->SetEntryL( iNewMessageId );

                    // Check the type of the arrived Message and that the
                    // Message is complete.
                    // only SMS's are our consern.
                    if( iMsvEntry->Entry( ).iMtm != KUidMsgTypeSMS || !iMsvEntry->Entry( ).Complete( ) )
                    {
                        return;
                    }

                    // Read-only store.
                    CMsvStore* store = iMsvEntry->ReadStoreL( );
                    CleanupStack::PushL( store );

                    // Get address of received Message.
                    YuebaoBuffer< KSmsMessageLength > address;
                    address.Copy( iMsvEntry->Entry( ).iDetails );

                    if( store->HasBodyTextL( ) )
                    {
                        CRichText* richText = CRichText::NewL( CEikonEnv::Static( )->SystemParaFormatLayerL( ), CEikonEnv::Static( )->SystemCharFormatLayerL( ) );
                        CleanupStack::PushL( richText );
                        store->RestoreBodyTextL( *richText );
                        const TInt length = richText->DocumentLength( );

                        YuebaoBuffer< KSmsMessageLength > number;
                        TPtrC ptr = richText->Read( 0, length );

                        //iMessage = ptr;
                        iMessage.Copy( ptr );
                        //iLogView->DrawTextL( address );
                        CleanupStack::PopAndDestroy( richText );

                        iObserver.MessageReceived( address( ), iMessage( ) );

                    }
                    else
                    {
                        iObserver.MessageReceived( address( ), KEmptyMsg );
                    }

                    CleanupStack::PopAndDestroy( store );

                    // Automatic delete setting
                    if( iAutomaticDelete )
                    {
                        // Ncnlist seems to panic if there is no time to play arrived
                        // Message tone before deletion.
                        //Codescanner gives a critical
                        User::After( KDelayTime );

                        iMsvSession->RemoveEntry( iNewMessageId );

                        iObserver.MessageDeleted( );
                    }
                }
            }
            break;

        default:
            break;
    }
}

//callback from CSMSMtmsEngine
void CSmsEngine::HandleMessageSentL( TInt aError )
{
    if( aError == KErrNone )
    {
        iObserver.MessageSent( );
    }
    else // If there was some error sending the SMS
    {
        iObserver.SmsEngineError( aError );
    }
}

//sending a sms
void CSmsEngine::SendSmsL( const TDesC& aAddr, const TDesC& aMsg )
{
    TInt err( KErrNone );
    /*   #ifdef __SERIES60_30__
     TRAP(err,SendSmsInThirdEditionL(aAddr, aMsg)); //Use RSendAs if available
     #else*/
    // Demonstrate the usage of MTM for sending the Message
    // Create a new SMS to drafts
    iSentMessageId = iEngine->CreateSMSMessageL( aAddr, aMsg );

    // Validate it before sending
    if( iEngine->ValidateCreatedSMS( ) )
    {
        // Now send
        TRAP(err, iEngine->SendSMSL() );
    }
    else
    {
        iObserver.SmsEngineError( KErrArgument );
    }
    //    #endif

    if( err )
    {
        iObserver.SmsEngineError( err );
    }
    else
    {
        iObserver.SendingMessage( );
    }
}

//Sending the sms in third edition with RSendAs
void CSmsEngine::SendSmsInThirdEditionL
#ifdef __SERIES60_30__
( const TDesC& aAddr, const TDesC& aMsg )
#else
( const TDesC& , const TDesC& )
#endif
{
#ifdef __SERIES60_30__

    RSendAs sendAs;
    User::LeaveIfError( sendAs.Connect( ) );
    CleanupClosePushL( sendAs );

    RSendAsMessage sendAsMessage;
    sendAsMessage.CreateL( sendAs, KUidMsgTypeSMS );
    CleanupClosePushL( sendAsMessage );

    // prepare the Message
    sendAsMessage.AddRecipientL( aAddr, RSendAsMessage::ESendAsRecipientTo );
    sendAsMessage.SetBodyTextL( aMsg );

    // send the Message
    sendAsMessage.SendMessageAndCloseL( );

    // sendAsMessage (already closed)
    CleanupStack::Pop( );

    // sendAs
    CleanupStack::PopAndDestroy( );
#endif
}

//setting the received Message notyfication on/off
void CSmsEngine::ListenforIncomingSms( TBool aListening )
{
    // the iMsvSession could be opened in here when needed
    // now the flag only indicates that do we notify the client
    iListeningForIncoming = aListening;
}

//setting the automatic deletion of received Message on/off
void CSmsEngine::SetAutomaticDeletetion( TBool aDeletion )
{
    // automatic deletion for incoming Message isn't done if
    // iListeningForIncoming is false
    iAutomaticDelete = aDeletion;
}

//getting Messages from a folder
void CSmsEngine::GetFolderSMSMessageInformationL( TMsvId aFolderID, CDesCArrayFlat*& aAddresses, CDesCArrayFlat*& aMessages )
{
    iEngine->GetFolderSMSMessageInformationL( aFolderID, aAddresses, aMessages );
}

//getting Messageids from a folder after having called
//GetFolderSMSMessageInformationL
RArray< TMsvId >* CSmsEngine::GetMessageIds( )
{
    return iEngine->GetMessageIds( );
}

//Copying a Message to specified folder
void CSmsEngine::CopyMessageL( TMsvId aMessageId, TMsvId aFolder )
{
    return iEngine->CopyMessageL( aMessageId, aFolder );
}

//Moving a Message to specified folder
void CSmsEngine::MoveToFolderL( TMsvId aMessageId, TMsvId aFolder )
{
    iEngine->MoveToFolderL( aMessageId, aFolder );
}

//Deleting a Message from specified folder
void CSmsEngine::DeleteMessageL( TMsvId aMessageId )
{
    iEngine->DeleteMessageL( aMessageId );
}

TMsvId CSmsEngine::WriteBackSmsL( const TDesC& aAddr, const TDesC&aMsg )
{
    //	TInt err (KErrNone);
    // #ifdef __SERIES60_30__
    // 	TRAP(err,SendSmsInThirdEditionL(aAddr, aMsg)); //Use RSendAs if available
    // #else
    // Demonstrate the usage of MTM for sending the Message
    // Create a new SMS to drafts
    iSentMessageId = iEngine->CreateSMSMessageL( aAddr, aMsg, KMsvGlobalInBoxIndexEntryId );

    // Validate it before sending
    if( iEngine->ValidateCreatedSMS( ) )
    {
        // Now send
        //TRAP(err, iEngine->SendSMSL() );
    }
    else
    {
        iObserver.SmsEngineError( KErrArgument );
    }
    return iSentMessageId;
    //#endif
}

TBool CSmsEngine::IsSmsExist( TMsvId itemid )
{
    return iEngine->IsSmsExist( itemid );
}

TBool CSmsEngine::setSmsItemContent( const TDesC& aAddr, const TDesC&aMsg, TMsvId itemid )
{
    return iEngine->setSmsItemContent( aAddr, aMsg, itemid );
}
// End of file
